<?php
//dezend by http://www.yunlu99.com/ QQ:270656184
class CI_Encrypt
{
	/**
	 * Reference to the user's encryption key
	 *
	 * @var string
	 */
	public $encryption_key = '';
	/**
	 * Type of hash operation
	 *
	 * @var string
	 */
	protected $_hash_type = 'sha1';
	/**
	 * Flag for the existence of mcrypt
	 *
	 * @var bool
	 */
	protected $_mcrypt_exists = false;
	/**
	 * Current cipher to be used with mcrypt
	 *
	 * @var string
	 */
	protected $_mcrypt_cipher;
	/**
	 * Method for encrypting/decrypting data
	 *
	 * @var int
	 */
	protected $_mcrypt_mode;

	public function __construct()
	{
		if ($this->_mcrypt_exists = function_exists('mcrypt_encrypt') === false) {
			show_error('The Encrypt library requires the Mcrypt extension.');
		}

		log_message('info', 'Encrypt Class Initialized');
	}

	public function get_key($key = '')
	{
		if ($key === '') {
			if ($this->encryption_key !== '') {
				return $this->encryption_key;
			}

			$key = config_item('encryption_key');

			if (!self::strlen($key)) {
				show_error('In order to use the encryption class requires that you set an encryption key in your config file.');
			}
		}

		return md5($key);
	}

	public function set_key($key = '')
	{
		$this->encryption_key = $key;
		return $this;
	}

	public function encode($string, $key = '')
	{
		return base64_encode($this->mcrypt_encode($string, $this->get_key($key)));
	}

	public function decode($string, $key = '')
	{
		if (preg_match('/[^a-zA-Z0-9\\/\\+=]/', $string) || (base64_encode(base64_decode($string)) !== $string)) {
			return false;
		}

		return $this->mcrypt_decode(base64_decode($string), $this->get_key($key));
	}

	public function encode_from_legacy($string, $legacy_mode = MCRYPT_MODE_ECB, $key = '')
	{
		if (preg_match('/[^a-zA-Z0-9\\/\\+=]/', $string)) {
			return false;
		}

		$current_mode = $this->_get_mode();
		$this->set_mode($legacy_mode);
		$key = $this->get_key($key);
		$dec = base64_decode($string);

		if (($dec = $this->mcrypt_decode($dec, $key)) === false) {
			$this->set_mode($current_mode);
			return false;
		}

		$dec = $this->_xor_decode($dec, $key);
		$this->set_mode($current_mode);
		return base64_encode($this->mcrypt_encode($dec, $key));
	}

	protected function _xor_decode($string, $key)
	{
		$string = $this->_xor_merge($string, $key);
		$dec = '';
		$i = 0;

		for ($l = self::strlen($string); $i < $l; $i++) {
			$dec .= $string[$i++] ^ $string[$i];
		}

		return $dec;
	}

	protected function _xor_merge($string, $key)
	{
		$hash = $this->hash($key);
		$str = '';
		$i = 0;
		$ls = self::strlen($string);

		for ($lh = self::strlen($hash); $i < $ls; $i++) {
			$str .= $string[$i] ^ $hash[$i % $lh];
		}

		return $str;
	}

	public function mcrypt_encode($data, $key)
	{
		$init_size = mcrypt_get_iv_size($this->_get_cipher(), $this->_get_mode());
		$init_vect = mcrypt_create_iv($init_size, MCRYPT_DEV_URANDOM);
		return $this->_add_cipher_noise($init_vect . mcrypt_encrypt($this->_get_cipher(), $key, $data, $this->_get_mode(), $init_vect), $key);
	}

	public function mcrypt_decode($data, $key)
	{
		$data = $this->_remove_cipher_noise($data, $key);
		$init_size = mcrypt_get_iv_size($this->_get_cipher(), $this->_get_mode());

		if (self::strlen($data) < $init_size) {
			return false;
		}

		$init_vect = self::substr($data, 0, $init_size);
		$data = self::substr($data, $init_size);
		return rtrim(mcrypt_decrypt($this->_get_cipher(), $key, $data, $this->_get_mode(), $init_vect), "\x00");
	}

	protected function _add_cipher_noise($data, $key)
	{
		$key = $this->hash($key);
		$str = '';
		$i = 0;
		$j = 0;
		$ld = self::strlen($data);

		for ($lk = self::strlen($key); $i < $ld; ++$i, ++$j) {
			if ($lk <= $j) {
				$j = 0;
			}

			$str .= chr((ord($data[$i]) + ord($key[$j])) % 256);
		}

		return $str;
	}

	protected function _remove_cipher_noise($data, $key)
	{
		$key = $this->hash($key);
		$str = '';
		$i = 0;
		$j = 0;
		$ld = self::strlen($data);

		for ($lk = self::strlen($key); $i < $ld; ++$i, ++$j) {
			if ($lk <= $j) {
				$j = 0;
			}

			$temp = ord($data[$i]) - ord($key[$j]);

			if ($temp < 0) {
				$temp += 256;
			}

			$str .= chr($temp);
		}

		return $str;
	}

	public function set_cipher($cipher)
	{
		$this->_mcrypt_cipher = $cipher;
		return $this;
	}

	public function set_mode($mode)
	{
		$this->_mcrypt_mode = $mode;
		return $this;
	}

	protected function _get_cipher()
	{
		if ($this->_mcrypt_cipher === NULL) {
			return $this->_mcrypt_cipher = MCRYPT_RIJNDAEL_256;
		}

		return $this->_mcrypt_cipher;
	}

	protected function _get_mode()
	{
		if ($this->_mcrypt_mode === NULL) {
			return $this->_mcrypt_mode = MCRYPT_MODE_CBC;
		}

		return $this->_mcrypt_mode;
	}

	public function set_hash($type = 'sha1')
	{
		$this->_hash_type = in_array($type, hash_algos()) ? $type : 'sha1';
	}

	public function hash($str)
	{
		return hash($this->_hash_type, $str);
	}

	static protected function strlen($str)
	{
		return defined('MB_OVERLOAD_STRING') ? mb_strlen($str, '8bit') : strlen($str);
	}

	static protected function substr($str, $start, $length = NULL)
	{
		if (defined('MB_OVERLOAD_STRING')) {
			isset($length) || ($length = (0 <= $start ? self::strlen($str) - $start : 0 - $start));
			return mb_substr($str, $start, $length, '8bit');
		}

		return isset($length) ? substr($str, $start, $length) : substr($str, $start);
	}
}

defined('BASEPATH') || exit('No direct script access allowed');

?>
